package com.gwtintegration.smartrf.builder;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import com.google.web.bindery.requestfactory.shared.BaseProxy;
import com.google.web.bindery.requestfactory.shared.RequestContext;
import com.gwtintegration.smartrf.client.Converter;
import com.gwtintegration.smartrf.client.ConverterCache;
import com.gwtintegration.smartrf.client.GenerateConverter;
import com.gwtintegration.smartrf.client.ProxyConverter;
import com.smartgwt.client.data.Record;
import com.smartgwt.client.data.RecordList;
import com.smartgwt.client.widgets.grid.ListGridRecord;

public class ConverterBuilder {

	private final List<ProxyConverterBuilder> proxyConverterBuilders;
	private String className;
	private String packageName;
	private final TreeLogger logger;
	private final GeneratorContext context;

	public ConverterBuilder(TreeLogger logger, GeneratorContext context, String typeName) throws UnableToCompleteException {

		this.logger = logger;
		this.context = context;
		this.proxyConverterBuilders = new ArrayList<ProxyConverterBuilder>();

		JClassType converterType = context.getTypeOracle().findType(typeName);
		if (converterType == null) {
			logger.log(Type.ERROR, "Unable to find type " + typeName + ".");
			throw new UnableToCompleteException();
		}

		packageName = converterType.getPackage().getName();
		className = converterType.getSimpleSourceName() + "Impl";

		JClassType baseProxyType = context.getTypeOracle().findType(BaseProxy.class.getName());
		if (baseProxyType == null) {
			logger.log(Type.ERROR, "Unable to find BaseProxy interface.");
			throw new UnableToCompleteException();
		}

		JClassType[] markedTypes = baseProxyType.getSubtypes();
		for (JClassType proxyType : markedTypes) {
			if (proxyType.isAnnotationPresent(GenerateConverter.class)) {
				proxyConverterBuilders.add(new ProxyConverterBuilder(this, proxyType));
			}
		}
	}

	public String build() throws UnableToCompleteException {

		PrintWriter printWriter = context.tryCreate(logger, packageName, className);
		if (printWriter == null) {
			return packageName + "." + className;
		}

		ClassSourceFileComposerFactory composerFactory = new ClassSourceFileComposerFactory(packageName, className);
		composerFactory.setSuperclass(Converter.class.getName());

		buildImports(composerFactory);

		SourceWriter sourceWriter = composerFactory.createSourceWriter(context, printWriter);
		if (sourceWriter == null) {
			return packageName + "." + className;
		}

		buildProxyConverters(sourceWriter);
		buildContructor(sourceWriter);

		sourceWriter.commit(logger);

		return packageName + "." + className;
	}

	private void buildImports(ClassSourceFileComposerFactory composerFactory) {

		composerFactory.addImport(IllegalArgumentException.class.getName());
		composerFactory.addImport(RequestContext.class.getName());
		composerFactory.addImport(ProxyConverter.class.getName());
		composerFactory.addImport(RecordList.class.getName());
		composerFactory.addImport(Record.class.getName());
		composerFactory.addImport(ListGridRecord.class.getName());
		composerFactory.addImport(ConverterCache.class.getName());

		for (ProxyConverterBuilder proxyConverterBuilder : proxyConverterBuilders) {
			composerFactory.addImport(proxyConverterBuilder.getProxyType().getQualifiedSourceName());
		}
	}

	private void buildProxyConverters(SourceWriter sourceWriter) throws UnableToCompleteException {
		for (ProxyConverterBuilder proxyConverterBuilder : proxyConverterBuilders) {
			proxyConverterBuilder.build(sourceWriter);
			sourceWriter.println();
		}
	}

	private void buildContructor(SourceWriter sourceWriter) {
		sourceWriter.println("public %s() {", className);
		sourceWriter.indent();

		for (ProxyConverterBuilder proxyConverterBuilder : proxyConverterBuilders) {
			sourceWriter.println("addProxyConverter(\"%s\", new %s());", proxyConverterBuilder.getProxyType().getQualifiedSourceName(),
					proxyConverterBuilder.getProxyConverterName());
		}

		sourceWriter.outdent();
		sourceWriter.println("}");
	}

	public String getClassName() {
		return className;
	}

	public String getPackageName() {
		return packageName;
	}

	public TreeLogger getLogger() {
		return logger;
	}

	public GeneratorContext getContext() {
		return context;
	}
}
